package com.hengyu.auth.interceptors;

import com.hengyu.auth.constants.YAuthConstants;
import com.hengyu.auth.entity.TokenInfoEntity;
import com.hengyu.auth.entity.UserInfoEntity;
import com.hengyu.auth.jpa.TokenInfoDAO;
import com.hengyu.auth.jpa.UserInfoDAO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * 权限拦截器
 * 根据配置请求路径获取header内的Authorization对应的jwt,Token值
 * ========================
 * Created with IntelliJ IDEA.
 * User：恒宇少年
 * Date：2017/3/27
 * Time：23:03
 * 码云：http://git.oschina.net/jnyqy
 * ========================
 */
public class YAuthInterceptor implements HandlerInterceptor
{
    /**
     * 请求之前
     * @param request 请求对象
     * @param response 返回对象
     * @param o
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        //如果是options请求是cors跨域预请求，设置allow对应头信息
        if(RequestMethod.OPTIONS.toString().equals(request.getMethod()))
        {
            response.setHeader("Access-Control-Allow-Origin","*");
            response.setHeader("Access-Control-Allow-Methods","*");
            response.setHeader("Access-Control-Allow-Headers","X-YAuth-Token");
            return true;
        }
        //自动排除生成token的路径
        if(request.getRequestURI().equals("/token"))
        {
            return true;
        }
        //其他请求获取头信息
        final String authHeader = request.getHeader("X-YAuth-Token");
        //如果没有header信息
        if (authHeader == null || authHeader.trim()=="") {
            throw new Exception("Missing or invalid X-YAuth-Token header.");
        }
        //截取"Bearer "后面的token
        //final String token = authHeader.substring(7); // The part after "Bearer "

        try {
            //获取jwt实体对象接口实例
            final Claims claims = Jwts.parser().setSigningKey(YAuthConstants.Y_AUTH)
                    .parseClaimsJws(authHeader).getBody();
            //获取内存中的token
            String memory_token = YAuthConstants.MEMORY_TOKENS.get(claims.getSubject());
            if(memory_token == null || memory_token.trim() == "") {
                //验证token是否在数据库中存在
                TokenInfoDAO tokenInfoDAO = getDAO(TokenInfoDAO.class, request);
                TokenInfoEntity tokenDBEntity = tokenInfoDAO.findOne(new Specification<TokenInfoEntity>() {
                    @Override
                    public Predicate toPredicate(Root<TokenInfoEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                        criteriaQuery.where(criteriaBuilder.equal(root.get("appId"), claims.getSubject()));
                        return null;
                    }
                });

                //验证appid是否存在数据库中
                UserInfoDAO userInfoDAO = getDAO(UserInfoDAO.class,request);
                UserInfoEntity userInfoEntity = userInfoDAO.findOne(new Specification<UserInfoEntity>() {
                    @Override
                    public Predicate toPredicate(Root<UserInfoEntity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                        criteriaQuery.where(criteriaBuilder.equal(root.get("appId"), claims.getSubject()));
                        return null;
                    }
                });
                if(userInfoEntity == null)
                {
                    throw new SignatureException("appId not found.");
                }
                //存放到内存
                YAuthConstants.MEMORY_TOKENS.put(userInfoEntity.getAppId(),new String(tokenDBEntity.getToken()));
                //设置临时变量
                memory_token = YAuthConstants.MEMORY_TOKENS.get(userInfoEntity.getAppId());
            }
            //验证token对应的appId是否存在内存中,并且判断内存中的token是否与客户端传来的一致
            if(!YAuthConstants.MEMORY_TOKENS.containsKey(claims.getSubject()) || !memory_token.equals(authHeader))
            {
                throw new SignatureException("Invalid appId.");
            }
            //验证token是否过期
            //过期时间
            long expiration = claims.getExpiration().getTime();
            //当前时间
            long current = System.currentTimeMillis();
            //时间戳
            long second = TimeUnit.MILLISECONDS.toSeconds(expiration - current);
            //如果时间戳小于等于0，证明已经失效
            if(second <= 0)
            {
                throw new SignatureException("timeout.");
            }
        }
        catch (final SignatureException e) {
            throw new Exception(e.getMessage());
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }

    /**
     * 根据传入的类型获取spring管理的对应dao
     * @param clazz 类型
     * @param request 请求对象
     * @param <T>
     * @return
     */
    private <T> T getDAO(Class<T> clazz,HttpServletRequest request)
    {
        BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
        return factory.getBean(clazz);
    }
}
